package com.prevpn.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.prevpn.api.AppApi;
import com.prevpn.api.RoleApi;
import com.prevpn.common.CommonResponse;
import com.prevpn.service.RoleRecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RoleRecommendationServiceImpl implements RoleRecommendationService {

    @Autowired
    private RoleApi roleApi;

    @Autowired
    private AppApi appApi;

    /**
     * 根据【应用名 / 域名 / IP / IP段(CIDR)】推荐角色
     *
     * body 示例：
     * {
     *   "directoryDomain": "local",
     *   "query": ["192.168.1.0/24", "控制中心", "https://www.cnki.net"]
     * }
     */
    @Override
    public CommonResponse<List<Map<String, Object>>> recommendRoles(Map<String, Object> body) {
        CommonResponse<List<Map<String, Object>>> resp = new CommonResponse<>();

        String directoryDomain = (String) body.getOrDefault("directoryDomain", "local");
        Object queryObj = body.get("query");

        // 支持 query 为 String 或 List<String>
        List<String> queries = new ArrayList<>();
        if (queryObj instanceof String) {
            queries.add(((String) queryObj).trim());
        } else if (queryObj instanceof List) {
            for (Object q : (List<?>) queryObj) {
                if (q != null) {
                    queries.add(q.toString().trim());
                }
            }
        }

        if (queries.isEmpty()) {
            resp.setCode("400");
            resp.setMsg("请输入应用名、域名、IP 或 IP 段");
            return resp;
        }

        // Step 1：查询所有应用（包括 WEB / 隧道）
        Map<String, Object> appQuery = new HashMap<>();
        appQuery.put("directoryDomain", directoryDomain); // 若后端不需要可忽略
        CommonResponse<Map<String, Object>> appResp = appApi.queryAppList(appQuery);

        if (!"OK".equalsIgnoreCase(appResp.getCode()) || appResp.getData() == null) {
            resp.setCode("500");
            resp.setMsg("查询应用列表失败：" + appResp.getMsg());
            return resp;
        }

        List<Map<String, Object>> apps = (List<Map<String, Object>>) appResp.getData().get("data");
        if (CollUtil.isEmpty(apps)) {
            resp.setCode("404");
            resp.setMsg("当前目录下无可用应用");
            return resp;
        }

        // Step 2：对每一个 query，在所有应用里做精确匹配（带匹配类型说明）
        List<Map<String, Object>> matchedResources = new ArrayList<>();
        for (String q : queries) {
            matchedResources.addAll(matchApplicationsWithDetail(q, apps));
        }

        if (matchedResources.isEmpty()) {
            resp.setCode("404");
            resp.setMsg("未找到匹配的应用、域名或 IP 范围");
            return resp;
        }

        // Step 3：查询角色列表
        Map<String, Object> roleQuery = new HashMap<>();
        roleQuery.put("directoryDomain", directoryDomain);
        CommonResponse<Map<String, Object>> roleResp = roleApi.queryRoleList(roleQuery);

        if (!"OK".equalsIgnoreCase(roleResp.getCode()) || roleResp.getData() == null) {
            resp.setCode("500");
            resp.setMsg("查询角色列表失败：" + roleResp.getMsg());
            return resp;
        }

        List<Map<String, Object>> roles = (List<Map<String, Object>>) roleResp.getData().get("data");
        if (CollUtil.isEmpty(roles)) {
            resp.setCode("404");
            resp.setMsg("未找到角色数据");
            return resp;
        }

        // Step 4：对每个角色，查角色详情，计算匹配的资源
        List<Map<String, Object>> result = new ArrayList<>();

        for (Map<String, Object> role : roles) {
            String roleName = (String) role.get("name");
            if (StrUtil.isBlank(roleName)) continue;

            // 查询角色详情，拿到 resourceIdList（角色关联的应用 ID）
            CommonResponse<Map<String, Object>> detail =
                    roleApi.queryRoleByName(directoryDomain, roleName, null);

            if (!"OK".equalsIgnoreCase(detail.getCode()) || detail.getData() == null) continue;

            List<String> resourceIds =
                    (List<String>) detail.getData().get("resourceIdList");
            if (CollUtil.isEmpty(resourceIds)) continue;

            // 找到这个角色拥有的应用名称列表（通过 ID 映射）
            Set<String> roleAppNames = new HashSet<>();
            for (String resourceId : resourceIds) {
                for (Map<String, Object> app : apps) {
                    if (resourceId.equals(app.get("id"))) {
                        String appName = (String) app.get("name");
                        if (StrUtil.isNotBlank(appName)) {
                            roleAppNames.add(appName);
                        }
                        break;
                    }
                }
            }

            if (roleAppNames.isEmpty()) continue;

            // 在匹配到的资源中，只保留这个角色拥有的应用
            List<Map<String, Object>> matchedForRole = matchedResources.stream()
                    .filter(r -> roleAppNames.contains(r.get("appName")))
                    .collect(Collectors.toList());

            if (!matchedForRole.isEmpty()) {
                Map<String, Object> item = new LinkedHashMap<>();

                // 按照指定顺序添加字段
                item.put("roleName", roleName);

                // 计算覆盖率：匹配资源数 / 角色总资源数
                double coverage = (double) matchedForRole.size() / resourceIds.size() * 100;
                item.put("coverage", String.format("%.1f%%", coverage));

                item.put("matched", matchedForRole.size());

                // 提取匹配的应用名称列表
                List<String> matchedAppNames = matchedForRole.stream()
                        .map(r -> (String) r.get("appName"))
                        .collect(Collectors.toList());
                item.put("matchedAppNames", matchedAppNames);

                item.put("grantedResources", matchedForRole);

                result.add(item);
            }
        }

        if (result.isEmpty()) {
            resp.setCode("404");
            resp.setMsg("未找到匹配角色");
            return resp;
        }

        // Step 5：按匹配数量从高到低排序
        result.sort((a, b) -> Long.compare(
                ((Number) b.get("matched")).longValue(),
                ((Number) a.get("matched")).longValue()
        ));

        resp.setCode("OK");
        resp.setMsg("角色推荐成功");
        resp.setData(result);
        return resp;
    }

    /* ========================== 应用匹配核心逻辑 ========================== */

    /**
     * 根据一个 query 在应用列表中查找匹配的应用，并标明是：
     * - "应用名称"
     * - "域名"
     * - "IP地址"
     * - "IP段"
     */
    private List<Map<String, Object>> matchApplicationsWithDetail(String query,
                                                                  List<Map<String, Object>> apps) {
        List<Map<String, Object>> matched = new ArrayList<>();
        if (StrUtil.isBlank(query)) return matched;

        String trimmedQuery = query.trim();
        String queryType = identifyQueryType(trimmedQuery);

        for (Map<String, Object> app : apps) {
            String appName = (String) app.get("name");
            String accessModel = (String) app.get("accessModel"); // WEB / L3VPN

            String appAddr = (String) app.get("appAddress");
            String accAddr = (String) app.get("accessAddress");

            // 1. 应用名称匹配
            if ("APP_NAME".equals(queryType)) {
                if (StrUtil.equalsIgnoreCase(appName, trimmedQuery)) {
                    matched.add(buildMatchDetail(appName, appName, "应用名称"));
                    continue;
                }
            }

            // 2. WEB 应用：域名/IP/段 匹配 appAddress / accessAddress
            if ("WEB".equalsIgnoreCase(accessModel)) {
                // 域名匹配
                if ("DOMAIN".equals(queryType)) {
                    if (isDomainExactMatch(appAddr, trimmedQuery)
                            || isDomainExactMatch(accAddr, trimmedQuery)) {
                        String host = extractHost(appAddr);
                        if (StrUtil.isBlank(host)) host = extractHost(accAddr);
                        matched.add(buildMatchDetail(appName,
                                StrUtil.blankToDefault(host, trimmedQuery),
                                "域名"));
                        continue;
                    }
                }

                // IP 精确匹配
                if ("IP".equals(queryType)) {
                    if (isIpAddressExactMatch(appAddr, trimmedQuery)
                            || isIpAddressExactMatch(accAddr, trimmedQuery)) {
                        matched.add(buildMatchDetail(appName, trimmedQuery, "IP地址"));
                        continue;
                    }
                }

                // CIDR 段匹配（WEB 场景：当 appAddress / accessAddress 是 IP）
                if ("CIDR".equals(queryType)) {
                    if (isIpInRangeFromAddress(appAddr, trimmedQuery)
                            || isIpInRangeFromAddress(accAddr, trimmedQuery)) {
                        matched.add(buildMatchDetail(appName, trimmedQuery, "IP段"));
                        continue;
                    }
                }
            }

            // 3. 隧道应用(L3VPN)：域名/IP/段 匹配 addressList.host
            if ("L3VPN".equalsIgnoreCase(accessModel)) {
                List<Map<String, Object>> addrList =
                        (List<Map<String, Object>>) app.get("addressList");
                if (CollUtil.isEmpty(addrList)) continue;

                boolean matchedThisApp = false;

                for (Map<String, Object> addr : addrList) {
                    String host = addr == null ? null : (String) addr.get("host");
                    if (StrUtil.isBlank(host)) continue;

                    // 3.1 域名匹配
                    if ("DOMAIN".equals(queryType) && isDomainExactMatch(host, trimmedQuery)) {
                        matched.add(buildMatchDetail(appName, host, "域名"));
                        matchedThisApp = true;
                        break;
                    }

                    // 3.2 IP 精确匹配
                    if ("IP".equals(queryType)) {
                        if (host.equals(trimmedQuery)) {
                            matched.add(buildMatchDetail(appName, trimmedQuery, "IP地址"));
                            matchedThisApp = true;
                            break;
                        }
                        // host 是 CIDR：192.168.1.0/24
                        if (host.contains("/") && isValidCIDR(host)
                                && isIpInRange(trimmedQuery, host)) {
                            matched.add(buildMatchDetail(appName, host, "IP段"));
                            matchedThisApp = true;
                            break;
                        }
                        // host 是 IP 范围：192.168.1.1-192.168.1.254
                        if (host.contains("-") && isIpInRangeRange(trimmedQuery, host)) {
                            matched.add(buildMatchDetail(appName, host, "IP段"));
                            matchedThisApp = true;
                            break;
                        }
                    }

                    // 3.3 CIDR 段匹配
                    if ("CIDR".equals(queryType)) {
                        // host 是 CIDR，直接相等或简单认为覆盖关系
                        if (host.contains("/") && isValidCIDR(host)) {
                            if (host.equals(trimmedQuery)) {
                                matched.add(buildMatchDetail(appName, host, "IP段"));
                                matchedThisApp = true;
                                break;
                            }
                            // 简单：判断 host 基 IP 在 query 段内，或 query 基 IP 在 host 段内
                            String baseQueryIp = trimmedQuery.split("/")[0];
                            String baseHostIp = host.split("/")[0];
                            if (isIpInRange(baseQueryIp, host)
                                    || isIpInRange(baseHostIp, trimmedQuery)) {
                                matched.add(buildMatchDetail(appName, host, "IP段"));
                                matchedThisApp = true;
                                break;
                            }
                        }
                        // host 是单 IP，判断是否落在 query 段内
                        if (!host.contains("/") && !host.contains("-") && isValidIPv4(host)
                                && isIpInRange(host, trimmedQuery)) {
                            matched.add(buildMatchDetail(appName, host, "IP段"));
                            matchedThisApp = true;
                            break;
                        }
                    }
                }

                if (matchedThisApp) continue;
            }
        }

        return matched;
    }

    /* ========================== 各类匹配小工具 ========================== */

    /** 构造匹配信息（不包含 resourceId） */
    private Map<String, Object> buildMatchDetail(String appName, String host, String type) {
        Map<String, Object> detail = new HashMap<>();
        detail.put("appName", appName);
        detail.put("matchedHost", host);
        detail.put("matchedType", type); // 应用名称 / 域名 / IP地址 / IP段
        return detail;
    }

    /** 域名精确匹配：从 URL 或 host 中提取域名后与 query 比较 */
    private boolean isDomainExactMatch(String addressOrHost, String queryDomain) {
        if (StrUtil.isBlank(addressOrHost) || StrUtil.isBlank(queryDomain)) return false;
        String host = extractHost(addressOrHost);
        //if (StrUtil.isBlank(host)) return false;
        //return host.equalsIgnoreCase(queryDomain);
        String queryHost = extractHost(queryDomain);
        if (StrUtil.isBlank(host) || StrUtil.isBlank(queryHost)) return false;
        return host.equalsIgnoreCase(queryHost);
    }

    /** IP 地址精确匹配：从 URL 或 host 中提取 IP 后比较 */
    private boolean isIpAddressExactMatch(String addressOrHost, String queryIp) {
        if (StrUtil.isBlank(addressOrHost) || StrUtil.isBlank(queryIp)) return false;
        String host = extractHost(addressOrHost);
        if (!isValidIPv4(host)) return false;
        return host.equals(queryIp);
    }

    /** 从地址中提取 IP，并判断是否在 CIDR 范围内（主要用于 WEB 场景） */
    private boolean isIpInRangeFromAddress(String addressOrHost, String cidr) {
        if (StrUtil.isBlank(addressOrHost) || StrUtil.isBlank(cidr)) return false;
        String host = extractHost(addressOrHost);
        if (!isValidIPv4(host) || !isValidCIDR(cidr)) return false;
        return isIpInRange(host, cidr);
    }

    /** 从 URL / host 中提取主机名或 IP */
    private String extractHost(String urlOrHost) {
        if (StrUtil.isBlank(urlOrHost)) return "";

        String s = urlOrHost.trim();

        // 如果看起来像 URL，尝试用 URI 解析
        if (s.startsWith("http://") || s.startsWith("https://")) {
            try {
                URI uri = new URI(s);
                String host = uri.getHost();
                if (host != null) return host;
            } catch (Exception ignored) {
            }
        }

        // 如果带协议头但 URI 解析失败，去掉协议再试
        if (s.matches("^(?i)https?://.+")) {
            s = s.replaceFirst("(?i)^https?://", "");
            int slashIndex = s.indexOf('/');
            if (slashIndex > 0) {
                s = s.substring(0, slashIndex);
            }
        }

        // 此时 s 可能就是 host / IP / 域名
        return s;
    }

    /** 智能识别 query 类型：IP / CIDR / 域名 / 应用名 / 其他 */
    private String identifyQueryType(String raw) {
        if (StrUtil.isBlank(raw)) return "UNKNOWN";
        String s = raw.trim();

        // 如果是 URL，先提取 host 再判断
        String hostPart = s;
        if (s.startsWith("http://") || s.startsWith("https://")) {
            hostPart = extractHost(s);
        }

        if (isValidIPv4(hostPart)) {
            return "IP";
        }
        if (isValidCIDR(hostPart)) {
            return "CIDR";
        }
        if (isValidDomain(hostPart)) {
            return "DOMAIN";
        }

        // 纯中文 / 英文 / 数字 / 下划线 / 横线 当作应用名
        if (hostPart.matches("^[\\u4e00-\\u9fa5A-Za-z0-9_\\-]+$")) {
            return "APP_NAME";
        }

        return "UNKNOWN";
    }

    /* ========================== IP / 域名 校验与计算 ========================== */

    /** 验证 IPv4 地址格式 */
    private static boolean isValidIPv4(String ip) {
        if (StrUtil.isBlank(ip)) return false;
        return ip.matches(
                "^((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.){3}" +
                        "(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$");
    }

    /** 验证 CIDR 格式，如 192.168.1.0/24 */
    private static boolean isValidCIDR(String cidr) {
        if (StrUtil.isBlank(cidr)) return false;
        String[] parts = cidr.split("/");
        if (parts.length != 2) return false;
        if (!isValidIPv4(parts[0])) return false;
        try {
            int prefix = Integer.parseInt(parts[1]);
            return prefix >= 0 && prefix <= 32;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /** 验证域名格式（简单版，允许 TLD 带数字） */
    private static boolean isValidDomain(String domain) {
        if (StrUtil.isBlank(domain)) return false;
        String d = domain.trim();
        // 必须至少有一个点，支持字母数字和 "-"
        return d.matches("^([A-Za-z0-9]([A-Za-z0-9\\-]{0,61}[A-Za-z0-9])?\\.)+[A-Za-z0-9]{2,}$");
    }

    /** IP 转 long，便于做区间比较 */
    private static long ipToLong(String ip) {
        String[] segs = ip.split("\\.");
        long res = 0;
        for (String s : segs) {
            res = (res << 8) + Integer.parseInt(s);
        }
        return res;
    }

    /** 判断指定 IP 是否在 CIDR 段内，如 192.168.1.10 ∈ 192.168.1.0/24 */
    private static boolean isIpInRange(String ip, String cidr) {
        if (!isValidIPv4(ip) || !isValidCIDR(cidr)) return false;
        String[] parts = cidr.split("/");
        String baseIp = parts[0];
        int prefix = Integer.parseInt(parts[1]);

        long ipVal = ipToLong(ip);
        long baseVal = ipToLong(baseIp);
        long mask = prefix == 0 ? 0 : (~((1L << (32 - prefix)) - 1) & 0xFFFFFFFFL);
        return (ipVal & mask) == (baseVal & mask);
    }

    /** 判断 IP 是否在一个 IP 区间内，如 192.168.1.10 ∈ 192.168.1.1-192.168.1.254 */
    private static boolean isIpInRangeRange(String ip, String range) {
        if (!isValidIPv4(ip) || StrUtil.isBlank(range) || !range.contains("-")) return false;
        String[] parts = range.split("-");
        if (parts.length != 2) return false;
        String startIp = parts[0].trim();
        String endIp = parts[1].trim();
        if (!isValidIPv4(startIp) || !isValidIPv4(endIp)) return false;

        long target = ipToLong(ip);
        long start = ipToLong(startIp);
        long end = ipToLong(endIp);

        if (start > end) {
            long tmp = start;
            start = end;
            end = tmp;
        }
        return target >= start && target <= end;
    }
}
